#include <sys/types.h>
#include <dirent.h>


#include "android_service/android_message.h"
#include "android_service/android_service.h"


int Android_Service::message_counter = 1000;

Android_Service::Android_Service(Client* _client, string _hostname, int port ) 
 : chanState(false), client(_client), port(port)
{
    hostname.assign(_hostname); 
    android_voice_pub = nh_.advertise<android_service::android>("android_voice", 100);
    android_map_pub = nh_.advertise<android_service::android>("android_map", 100);
    //android_bio_pub = nh_.advertise<android_service::android>("android_bio", 100); actionlib
    android_sub = nh_.subscribe("android_request",1, &Android_Service::android_callback, this);
  
    //TODO - ros defaults to ~/.ros, but we need to store our images somewhere else -  
    /*
    if(getcwd(cwd_path, PATH_MAX) == NULL) {
        cout <<"info: Current Working Path not defined, "<<endl;
        cout <<"    you may have supply full paths were necessary"<<endl; 
    }
    */
    bio_base_path = "/home.local/robotlab/robotic-photographer/android_service"; 
    
}
Android_Service::~Android_Service()
{
    
}

int Android_Service::open() {
    port = port;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { 
        cout <<"error opening socket"<<endl;
        return -1;
    }
    server = gethostbyname(hostname.c_str());
    if (server == NULL) {
        cout <<"error, no such host"<<endl;;
        return -1;
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(port);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
        cout <<"error connecting"<<endl;
        return -1;
    }

    // ---Not sure why adb is not picking messages from HTC on time - 
    unsigned int opt = 4, one = 1;
    setsockopt(sockfd, IPPROTO_TCP,TCP_NODELAY,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, IPPROTO_TCP,SO_KEEPALIVE,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
 
    Message hMsg;
    int ret = recv_msg(&hMsg, 15);
    if(ret == ANDROID_TIMEOUT) {
        cout <<"error: We [ TIMEDOUT ] wating for Android Server intro - exiting....."<<endl;
	return -1;
    } else
    if(ret == SOCKET_CLOSED) {
        cout <<"error: We found Android Server [ SOCKET_CLOSED ] - exiting....."<<endl;
        return -1;
    }
    cout <<"info: Received Intro [ "<<hMsg.str()<<" ] ...."<<endl;
    chanState = true;
    return 0;
}

int Android_Service::init_vocabulary(string filename) {
    char word[61];
    string fp = bio_base_path+"/dict/"+filename;
    ifstream wordlist;
    wordlist.open(fp.c_str(),ifstream::in); 
    if(wordlist.is_open()) {
        while(!wordlist.getline(word, 60).eof()) {
            harris_vocabulary.insert(pair<string,bool>(buffer,true));
        }
        if(harris_vocabulary.size() <= 0) {
            cout <<"error: Vocabulary file [ "<<filename<<" ] is empty...."<<endl;
            return -1;
        } 
        cout <<"info: harris vocabulary[ "<<harris_vocabulary.size()<<" ] well initialized........"<<endl;
        return 0;
    } else {
    	cout <<"error: opening dict file [ "<<fp<<" ] - needed to init Harris vocabulary" << endl;
    }
    return -1;
}
    
int Android_Service::send_msg(Message *hMsg) {
    if((hMsg->message_type() <= 0) || (hMsg->message_type() > ANDROID_USER_LIMIT)) {
        return MALFORMED_MSG;
    } 
    hMsg->add_entry("\n");
    int msg_len = hMsg->android_str().length();
    int n = write(sockfd,hMsg->android_str().c_str(),msg_len);
    if(n <= 0) { 
        chanState = false;
        return SOCKET_CLOSED;
    }
    return SUCCESS;
}
int Android_Service::recv_msg(Message* hMsg, int timeout) {
    
    int ret;

    fd_set data;
    struct timeval tv;
    tv.tv_sec  = timeout;
    tv.tv_usec = 0;
    FD_ZERO(&data);
    FD_SET(sockfd, &data);
        errno = 0;
    ret = select(FD_SETSIZE, &data,NULL,NULL,&tv);
    if(ret < 0) {
        cout <<"error:recv_msg("<<errno<<"): select() - Failed"<<endl;
        return SOCKET_CLOSED;
    } else if(ret == 0) {
        return ANDROID_TIMEOUT;
    } else if(FD_ISSET(sockfd,&data)) {
        memset(buffer,0,MAX_MSG_SIZE);
        errno = 0;
        ret = recv(sockfd,buffer,MAX_MSG_SIZE,0);
        if(ret <= 0) {
            cout <<"error: recv_msg("<<errno<<") - Network partner not available..."<<endl;
            chanState = false;
            return SOCKET_CLOSED;
        }
        buffer[ret-1] = '\0';
        hMsg->read(buffer, true);
        //cout <<"   raw-msg[ "<<buffer<<" ] "<<endl;
        //cout <<"android-msg[ "<<hMsg->str()<<" ] "<<endl;
    }
    return SUCCESS;
}

int Android_Service::android_action(Message& msg, Message& resp, int ttl) {
        
    cout <<"----androidBridge("<<msg.size()<<")------------"<<endl;
    if(msg.message_type() != ANDROID_UNDEFINED) {
        // push user message to android 
        cout <<"Sending MSG[ "<<msg.android_str()<<" ]...."<<endl;
        int ret = send_msg(&msg);
        if(ret != SUCCESS) {
            cout <<"error: send_msg() : Failed to send message....exiting..."<<endl;
            return ANDROID_SEND_ERROR;
        }
        // wait for response for all non-broadcast messages - 
        if(msg.message_type() == ANDROID_BROADCAST) {
            sleep(2); 
        } else 
        if(!(msg.message_type() == ANDROID_BROADCAST)) {
            Message msgIn;
            // read response from Android - 
            ret = recv_msg(&resp, ttl);
            if(ret == ANDROID_TIMEOUT) { 
                cout <<"info: no messages to read.....sleeping some more- "<<endl;
                return ANDROID_TIMEOUT;
            }
            if(ret == SOCKET_CLOSED) {
                cout <<"error: send_msg() : Failed to send message....exiting..."<<endl;
                chanState = 0;
                return SOCKET_CLOSED;
            }
            //if(msgIn.message_type() != ANDROID_UNDEFINED) {
            if(resp.message_type() == ANDROID_BIOMETRICS) {
                showBiometrics(resp);
            } else {
                cout <<"\nVoiceBox:: [ "<<resp.str()<<" ]"<<endl;
            }
        }
        //msg.reset();
        return SUCCESS;
    }
    return -1;  
}
int Android_Service::android_action_bio(Message& bioMsg) {
    cout <<"bioMsg[ "<<bioMsg.str()<<" ] "<<endl;
    int f_count = 0;
    stringstream strbuf;
    string path = bio_base_path + "/"+bioMsg.entry(2);
    cout <<"action_bio_path [ "<<path<<" ] "<<endl;
    DIR *dirp = opendir(path.c_str());
    struct dirent *dp;
    if(!dirp) {
        cout <<"error: push() - Faces directory inaccessible..."<<endl;
        return -1;
    }
    // push folder to android device
    string file_path = "sudo adb push "+ path + " /sdcard/faces/";
    int ret = system(file_path.c_str());
    if(ret == -1) { 
        //TODO - Reponds with failure to the database -       
        return ret; 
    }

    cout <<"info: adb-cmd [ "<<file_path<<" ] "<<endl;

    // now compute the biometic signatures 
    while ((dp = readdir(dirp)) != NULL) {
        if(strlen(dp->d_name) > 3) {
            Message _msg(ANDROID_BIOMETRICS);
            Message _rmsg;
            strbuf <<ANDROID_BIOMETRICS<<":"<<bioMsg.entry(0)<<":"<<bioMsg.entry(1)
                <<":/sdcard/faces/"<<dp->d_name;
            _msg.read(strbuf.str().c_str(), false);
            cout <<"info: bioCompute [ "<< _msg.str()<<" ] "<<endl;
   
            // send for android computation -  
            android_action(_msg, _rmsg, 25);
           
            // Pack the Goal and send over  
            android_service::storage_msgGoal goal;
            for(int i = 1; i < _rmsg.size(); i++) { 
                goal.record.push_back(_rmsg.entry(i++));
            }
            client->sendGoal(goal);

            //cout <<"info:"<<path<<"/"<<dp->d_name<<endl;
            _msg.reset();
            _rmsg.reset();
            strbuf.str("");
            f_count++;
        }
    }
    closedir(dirp);
    cout <<"info: [ "<<f_count<<" ] Facial-Bio-Signatures Computed by android..."<<endl;
    return (f_count > 0) ? SUCCESS : -1;
}

void Android_Service::showBiometrics(Message& msg) {
    int i = 0;
    stringstream strbuf;
    if(msg.size() < 7) {
        cout <<"error: Malformed biometric msg[ "<<msg.str()<<" ] "<<endl;
        return;
    }
    strbuf <<"Facial Biometric Signature:"<<endl;
    strbuf <<"         MsgType[ ANDROID_BIOMETRICS "<<endl;
    strbuf <<"        UserName[ "<<msg.entry(i++)<<endl;
    strbuf <<"           MsgId[ "<<msg.entry(i++)<<endl;
    strbuf <<"Confidence Level[ "<<msg.entry(i++)<<endl;
    strbuf <<"    EyesDistance[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.x[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.y[ "<<msg.entry(i++)<<endl;
    strbuf <<"    imageWidth.y[ "<<msg.entry(i++)<<endl;
    strbuf <<"   imageHeight.y[ "<<msg.entry(i++)<<endl;
    strbuf <<"        imageUrl[ "<<msg.entry(i++)<<endl;
    cout <<strbuf.str()<<endl;
}

void Android_Service::usage() {
    string usage = "usag()::\n   VoiceClient  -h\n";
    usage += "   VoiceClient <msgType>:<clientid>:<field1>:<field2>...<fieldn>\n";
    usage += "      where msgTye {\n";
    usage += "\t[3] ANDROID_BROADCAST,  // one way voice message - \n";
    usage += "\t[4] ANDROID_QUESTION,   // Question with replay - \n";
    usage += "\t[5] ANDROID_GRID_LOCATION   // Ask humam for a desired map location - \n";
    usage += "\t[6] ANDROID_BIOMETRICS // ask Android to generate facial biometric signature - \n";
    usage += "      }\n\n";
    usage += "      e.g. 3:zeddy:Ask humam for a desired map location\n";
    usage += "      e.g. 4:zeddy:Could you help me please?\n";
    usage += "      e.g. 5:zeddy:Where is west point? Touch it on my touch Tablet.\n";
    usage += "      e.g. 6:zeddy:data\n";
    usage += "----------------------------------------------------------"; 
    cout <<usage<<endl;
}
string Android_Service::itos(int i) {
    stringstream ss;
    ss << i;
    return ss.str();
}

// ros start here - 

void Android_Service::android_packMsg(Message *androidMsg, android_service::android *rosMsg) {
    rosMsg->msg_type = androidMsg->message_type();
    for(int i = 0; i < androidMsg->size(); i++) {
        rosMsg->record.push_back(androidMsg->entry(i));
    }
} 

void Android_Service::android_callback(const android_service::android& msg) 
{
    if((msg.msg_type <=0 ) || (msg.msg_type > ANDROID_USER_LIMIT)) {
        cout <<"error: Recieved bad message [ "<<msg.msg_type<<" ] "<<endl;
        return;
    }
    Message android_msg(msg.msg_type), android_rmsg;
    android_service::android ros_msg;

    for(uint32_t i =0; i < msg.record.size(); i ++) {
        android_msg.add_entry(msg.record[i]);
    }
    cout <<"info:android_callback [ "<<android_msg.str()<<" ] "<<endl;
    while(true) {
        switch(android_msg.message_type()) {
            case ANDROID_BROADCAST:      // one way voice message - 
                cout <<"info:android_callback [ ANDROID_BROADCAST ] "<<endl;
                android_action(android_msg,android_rmsg,4); 
                break;
            case ANDROID_QUESTION:       // Question with replay - 
                cout <<"info:android_callback [ ANDROID_QUESTION ] "<<endl;
                android_action(android_msg,android_rmsg,15);
                android_packMsg(&android_rmsg, &ros_msg);
                android_voice_pub.publish(ros_msg);  
                break;
            case ANDROID_GRID_LOCATION:  // Ask human for a desired map location - 
                cout <<"info:android_callback [ ANDROID_GRID_LOCATION ] "<<endl;
                android_action(android_msg,android_rmsg,15); 
                android_packMsg(&android_rmsg, &ros_msg);
                android_map_pub.publish(ros_msg);  
                break;
            case ANDROID_BIOMETRICS:     // compute facial biometric signature on this face - 
                cout <<"info:android_callback [ ANDROID_BIOMETRICS ] "<<endl;
                android_action_bio(android_msg); 
                break;
            default:
                cout <<"error: service_type("<<android_msg.message_type()<<") ...."<<endl;
        }
        break;
    }
}


int main(int argc, char** argv)
{
    //srand ( time(NULL) );
    ros::init(argc, argv, "Android_Service");

    Client client("storage_msg",true);
    Android_Service asv(&client, string("localhost"), 7500);
    if(asv.init_vocabulary(string("wordlist.txt")) != 0) {
   	cout <<"halting android_service package ......."<<endl;
	return 0;	 
    }
    //    open to activate android comms ***
    if(asv.open() != 0) {
        cout <<"error: Can't connect to the server....exiting..."<<endl<<endl;
        return 0;
    }
    ros::spin();
    return 0;
}
